package com.gitee.wsl.text.predicate

fun interface StringMatcher {
    /**
     * 从[index]位置开始解析字符串[s]，返回解析后的位置集合，用Sequence表示
     */
    fun parse(s: String, index: Int): Sequence<Int>

    companion object
}

/**
 * 判断字符串[s]是否与当前模式匹配
 */
fun StringMatcher.match(s: String) = parse(s, 0).any { i -> i == s.length }

/**
 * 连接两个Matcher
 */
infix fun StringMatcher.and(rhs: StringMatcher) = StringMatcher { s, index ->
    parse(s, index).flatMap { i -> rhs.parse(s, i) }.distinct()
}

/**
 * 将字符串[s]转换成Matcher，并与当前Matcher相连
 */
infix fun StringMatcher.and(s: String) = and(StringMatcher.str(s))

/**
 * 将字符[c]转换成Matcher，并与当前Matcher相连
 */
infix fun StringMatcher.and(c: Char) = and(StringMatcher.ch(c))

/**
 * 使用or连接两个Matcher
 */
infix fun StringMatcher.or(rhs: StringMatcher) = StringMatcher { s, index ->
    (parse(s, index) + rhs.parse(s, index)).distinct()
}

/**
 * 将字符串[s]转换成Matcher，并使用or与当前Matcher相连
 */
infix fun StringMatcher.or(s: String) = or(StringMatcher.str(s))

/**
 * 将字符[c]转换成Matcher，并使用or与当前Matcher相连
 */
infix fun StringMatcher.or(c: Char) = or(StringMatcher.ch(c))

/**
 * 将当前Matcher连续应用至少[minTimes]次
 */
fun StringMatcher.many(minTimes: Int = 0) = StringMatcher { s, index ->
    sequence {
        // 应用minTimes次
        var seq = sequenceOf(index)
        repeat(minTimes) {
            seq = seq.flatMap { i -> parse(s, i) }.distinct()
        }

        val visited = HashSet<Int>()
        seq.forEach { i ->
            yield(i)
            visited.add(i)
        }

        // 继续应用当前解析器，直到没有新的位置产生
        val queue = ArrayDeque(visited)
        while (!queue.isEmpty()) {
            for (i in parse(s, queue.removeFirst())) {
                if (!visited.contains(i)) {
                    yield(i)
                    visited.add(i)
                    queue.addLast(i)
                }
            }
        }
    }
}

/**
 * 将当前Matcher连续应用1次或多次
 */
fun StringMatcher.many1() = many(1)

/**
 * 将当前Matcher重复最少[minTimes]次，最多[maxTimes]次
 */
fun StringMatcher.repeat(minTimes: Int, maxTimes: Int) = StringMatcher { s, index ->
    sequence {
        // 应用minTimes次
        var set = sequenceOf(index)
        repeat(minTimes) {
            set = set.flatMap { i -> parse(s, i) }.distinct()
        }

        val visited = HashSet<Int>()
        set.forEach { i ->
            yield(i)
            visited.add(i)
        }

        // 继续应用直到maxTimes次
        val queue = ArrayDeque(visited)
        var times = minTimes
        while (!queue.isEmpty() && times < maxTimes) {
            val cnt = queue.size
            repeat(cnt) {
                for (i in parse(s, queue.removeFirst())) {
                    if (!visited.contains(i)) {
                        yield(i)
                        visited.add(i)
                        queue.addLast(i)
                    }
                }
            }
            times++
        }
    }
}

/**
 * 将当前解析器重复[times]次
 */
fun StringMatcher.repeat(times: Int) = repeat(times, times)

/**
 * 应用当前Matcher，并调用[mapper]生成下一个Matcher，继续应用下一个Mapper
 */
fun StringMatcher.flatMap(mapper: (String) -> StringMatcher) = StringMatcher { s, index ->
    parse(s, index).flatMap { i ->
        val matchStr = s.substring(index..<i)
        mapper(matchStr).parse(s, i)
    }.distinct()
}

/**
 * 匹配满足条件的单个字符，条件由[predicate]指定
 */
fun StringMatcher.Companion.ch(predicate: (Char) -> Boolean) = StringMatcher { s, index ->
    if (index < s.length && predicate(s[index])) {
        sequenceOf(index + 1)
    } else {
        emptySequence()
    }
}

/**
 * 匹配字符集[chs]内的字符
 */
fun StringMatcher.Companion.chs(vararg chs: Char): StringMatcher {
    val set = chs.toSet()
    return ch { c -> set.contains(c) }
}

/**
 * 匹配字符[c]
 */
fun StringMatcher.Companion.ch(c: Char) = ch { ch -> ch == c }

/**
 * 匹配任意字符
 */
val StringMatcher.Companion.any: StringMatcher
    get() = ch { true }

/**
 * 匹配不等于[c]的字符
 */
fun StringMatcher.Companion.not(c: Char) = ch { ch -> ch != c }

/**
 * 匹配范围[[c1], [c2]]内的字符
 */
fun StringMatcher.Companion.range(c1: Char, c2: Char) = ch { c -> (c - c1) * (c - c2) <= 0 }

/**
 * 匹配指定字符串[str]
 */
fun StringMatcher.Companion.str(str: String) = StringMatcher { s, index ->
    if (s.startsWith(str, index)) {
        sequenceOf(index + str.length)
    } else {
        emptySequence()
    }
}

/**
 * 匹配集合[strs]内的字符串
 */
fun StringMatcher.Companion.strs(vararg strs: String) = strs.map(::str).reduce(StringMatcher::or)

/**
 * 将多个Matcher用and连接
 */
fun seq(vararg matchers: StringMatcher) = matchers.reduce(StringMatcher::and)

/**
 * 将多个Matcher用or连接
 */
fun StringMatcher.Companion.oneOf(vararg matchers: StringMatcher) = matchers.reduce(StringMatcher::or)

/**
 * 惰性Matcher，解析时调用[matcherSupplier]获取Matcher实例并执行
 */
fun StringMatcher.Companion.lazy(matcherSupplier: () -> StringMatcher) = StringMatcher { s, index -> matcherSupplier().parse(s, index) }

fun Char.many(minTimes: Int = 0) = StringMatcher.ch(this).many(minTimes)
fun Char.many1() = StringMatcher.ch(this).many1()
infix fun Char.and(matcher: StringMatcher) = StringMatcher.ch(this).and(matcher)
infix fun Char.or(matcher: StringMatcher) = StringMatcher.ch(this).or(matcher)

fun String.many(minTimes: Int = 0) = StringMatcher.str(this).many(minTimes)
fun String.many1() = StringMatcher.str(this).many1()
infix fun String.and(matcher: StringMatcher) = StringMatcher.str(this).and(matcher)
infix fun String.or(matcher: StringMatcher) = StringMatcher.str(this).or(matcher)
